JavaScript中对象大致可以分为三类:
- 内置对象,是由ECMAScript规范定义的对象或类(构造函数)。例如:数组,函数,日期和正则表达式等
- 宿主对象,是由JavaScript解释器所嵌入的宿主环境(比如Web浏览器)定义的,宿主对象可以当作内置对象看待
- 自定义对象,是由运行中的JavaScript代码创建的对象
对象中的属性分为两类:
- 自有属性,直接在对象中定义的属性
- 继承属性,是在对象的原型对象中定义的属性
对象创建
对象直接量
|
|
通过new创建对象
关键字new后跟随一个函数调用,这里的函数调用称作构造函数
通过Object.create()创建
Object.create()是一个静态函数,而不是提供给某个对象调用的方法。
使用Object.create()创建对象的方法很简单,只需传入所需的原型对象即可,例如:
这里对象o继承了原型对象{x:1, y:2}对象的x和y属性
可以通过传入null来创建一个没有原型的新对象,通过这种方式创建的对象不会继承任何东西,甚至不包括基础方法,比如toString(),也就是说它甚至不能和“+”运算符一起正常工作,例如:
var o2 = Object.create(null);
属性的查询和设置
访问对象的属性可以通“.”或者“[]”运算符来获取属性的值
通过“.”访问对象属性时,程序无法动态指定属性名
当通过“[]”访问属性时,属性名通过字符串来表示,程序可以动态指定属性名
var attr = " ";
for(var i = 1; i <= 10 ; 1++){
attr += customer["attr"+1];
}
原型
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Object.prototype获得对原型对象的引用
通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。例如通过new Array()创建的对象的原型就是Array.prototype
删除属性
delete运算符只能删除自有属性,不能删除继承属性
当delete表达式删除成功或没有任何副作用时,它返回true。如果delete后不是一个属性访问表达式,delete同样返回true,例如:
o = {x:1};
delete o.x // return true
delete o.toString; //also return true
delete 1 // 没有意义 return ture;
检测属性
hasOwnProperty()
hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性,例如:
var o = {x: 1};
o.hasOwnProperty("x"); // return true;
o.hasOwnProperty("y"); // return false;
o.hasOwnProperty("toString") //toString是继承属性,返回false
propertyIsEnumerable()
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到时自有属性且这个属性的可枚举性(enumerable attribute)为true时它才返回true
in和!=
var o = {x: 1};
o.x !== undefined; // x属性存在但是不为undefined
o.y !== undefined; // y属性不存在 return false
x in o // return true
y in o // return false
delete o.x
x in o // x属性已被删除 return false
属性枚举
for/in循环
通常使用for/in循环遍历对象可枚举属性,例如:
var o = {x: 1, y: 2};
o.propertyIsEnumerable("toString"); //return false
for(p in o){
console.log(p)
}
Object.keys()
Object.keys()返回一个数组,这个数组由对象中可枚举的自有属性的名称组成
Object.getOwnPropertyNames()
Object.getOwnPropertyNames()只会返回对象的所有自有属性名称,而不仅仅是可枚举的属性
属性getter和setter(ECMAScript5)
在ECMAScript5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称作“存取器属性”(accessor property),存取器属性可以用对象直接量语法的一种扩展语法进行定义:
var o = {
value: 1,
get getValue(){
return this.value;
},
set setVlaue(value){
this.value = value;
},
get getData(){
this.value;
}
}
console.log(o.getValue);
console.log(o.getData);
存取器属性定义为一个或两个和属性同名的函数,这个函数定义没有使用function关键字,而是使用get和set,注意没有使用冒号将属性名和函数体分开
和数据属性一样,存取器属性也可以继承,例如:
var p = new Object();
p.prototype = o;
console.log(p.getValue)
属性的特性
除了包含名字和值之外,属性还包含一些标识它们可写、可枚举、可配置的特性,在ECMAScript3中无法设置这些特性,所有通过ECMAScript3的程序创建的属性都是可写的、可枚举、可配置的,且无法对这些特性进行修改。在ECMAScript5中提供了修改和查询这些属性的API,这些API在某些场景中将发挥极其重要的作用,例如:
- 可以用过这些API给原型对象添加方法,并将它们设置成不可枚举的,这让它们看起来更像内置方法
- 可以通过这些API给对象定义不能修改或删除的属性,借此“锁定”这个对象
我们可以把“属性特性”看成数据属性的值,也可以把“属性特性”的看作是存取器属性的getter和setter方法,
属性特性的数据属性
属性特性看成数据属性值,可认为一个属性包含一个名字和4个特性,数据属性的4个特性分别是它的值、可写性、可枚举性和可配置性。
属性特性的存取器属性
属性特性看成存取器属性时,属性特性不具有值和可写性。其中可写性是由setter方法存在与否决定的。因此4个特性是读取值(get)、写入值(set)、可枚举性和可配置型
为了实现属性特性的查询和设置操作,ECMAScript5中定义了一个名为“属性描述符”的对象,这个对象代表那4个特性
描述符对象的属性和它们所描述的属性特性是同名的,因此数据属性的描述符对象的属性有value、writable、enumerable、configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor({x:1}, "x");
//return {value: 1, writable: true, enumerable: true, configurable: true};
Object.defineProperty()
要想设置属性的特性,或者想让新建属性具有某种特性,则可以调用Obejct.defineProperty(),传入要修改的对象、要创建或修改的属性的名称以及属性描述符对象
var o = {};
Obejct.defineProperty(o, "x", {value: 1,
writable: true,
enumerable: false,
configurable: true});
// 这样x属性时存在的,但是不可枚举
console.log(o.x); //=> 1
Object.keys(o) //=> []
对象的三个属性
每一个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible)
原型属性
对象的原型属性是用来继承属性的,在ECMAScript5中可以适用Obejct.getPrototypeOf()查询某个对象的原型
可以适用isPrototypeOf()方法检测p是否是o的原型,例如:
var p = {x:1};
var o = Obejct.create(p);
p.isPrototypeOf(o);
类属性
对象的类属性是一个字符串,用以表示对象的类型信息。ECMAScript3和ECMASCript5都未提供设置这个属性的方法,并只有一个间接的方式可以查询它。默认的toString()方法,返回如下格式字符串
[object class]
通过内置构造函数(比如Array和Date)创建的对象包含“类属性”,它与构造函数名称相匹配。宿主对象也包含有意义的“类属性”,但这和具体的JavaScript实现有关。通过对象直接量和Obejct.create创建的对象的类属性都是“Obejct”,那些自定义构造函数创建的对象也是一样,类属性都是“Obejct”
可扩展性
对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显式扩展的,宿主对象的可扩展性是由JavaScript引擎定义的。
在ECMAScript5中,所有的内置对象和自定义对象都是可扩展的,除非将它们转换为不可扩展,同样,宿主对象的可扩展性也是由实现ECMAScript5的JavaScript引擎决定的
一旦将对象转换为不可扩展的,就无法再将其转换为可扩展的了
Obejct.seal()和Obejct.preventExtensions()
Obejct.seal()和Obejct.preventExtensions()类似,除了能够将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的,也就是不能给这个对象添加新属性,而且它已有属性也不能删除或配置
对象序列化
ECMAScript5提供内置函数JSON.stringify()和JSON.parse()用来序列化和还原JavaScript对象。
JSON.stringify()只能序列化对象可枚举的自有属性
对象方法
toString()方法
toString()将返回一个表示调用这个方法的对象值得字符串。在需要将对象对象转换为字符串的时候,JavaScript都会调用这个方法
默认toString()方法的返回值带有的信息量很少,因此很多类都带有自定义的toString(),例如数组转换为字符串的时候。
toLocaleString()方法
对象都包含toLocaleString()方法,该方法返回一个表示这个对象的本地化字符串,Obejct中默认的toLocaleString()方法并不会做任何本地化的自身操作,仅调用toString()方法并返回对应值。Date和NUmber类对toLocaleString()方法做了定制,可以同他们对数字、日期和时间做本地化的转换。
toJSON()方法
Object并没有定义toJSON(),JSON,但Stringify()会调用toJSON()方法
valueOf()方法
valueOf()方法和toString()方法非常类似,但往往当JavaScript需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换数字的时候,有些内置类自定义了valueOf()方法,比如Date.valueOf()